home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / daemons / nfs / nfs-serv.2be / nfs-serv / nfs-server-2.2beta16 / fh.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-28  |  28.6 KB  |  1,237 lines

  1. /*
  2.  * fh        This module handles the file-handle cache.
  3.  *        FILE HANDLE PACKAGE FOR USER-LEVEL NFS SERVER
  4.  *
  5.  *        Interfaces:
  6.  *            pseudo_inode
  7.  *            mostly used internally, but also called from unfsd.c
  8.  *            when reporting directory contents.
  9.  *            fh_init
  10.  *            Initializes the queues and 'flush' timer
  11.  *            fh_pr
  12.  *            debugging primitive; converts file handle into a
  13.  *            printable text string
  14.  *            fh_create
  15.  *            establishes initial file handle; called from mount
  16.  *            daemon
  17.  *            fh_path
  18.  *            returns unix path corresponding to fh
  19.  *            fh_fd
  20.  *            returns open file descriptor for given file handle;
  21.  *            provides caching of open files
  22.  *            fd_idle
  23.  *            provides mututal exclusion of normal file descriptor
  24.  *            cache use, and alarm-driven cache flushing
  25.  *            fh_compose
  26.  *            construct new file handle from existing file handle
  27.  *            and directory entry
  28.  *            fh_psi
  29.  *            returns pseudo_inode corresponding to file handle
  30.  *            fh_remove (new, by Don Becker)
  31.  *            delete the file handle associated with PATH from the
  32.  *            cache
  33.  *
  34.  * Authors:    Mark A. Shand, May 1988
  35.  *        Donald J. Becker <becker@super.org>
  36.  *        Rick Sladkey <jrs@world.std.com>
  37.  *        Patrick    Sweeney <pjs@raster.Kodak.COM>
  38.  *        Orest Zborowski <obz@raster.Kodak.COM>
  39.  *        Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  40.  *        Olaf Kirch, <okir@monad.swb.de>
  41.  *
  42.  *        Copyright 1988 Mark A. Shand
  43.  *        This software maybe be used for any purpose provided
  44.  *        the above copyright notice is retained.  It is supplied
  45.  *        as is, with no warranty expressed or implied.
  46.  */
  47.  
  48. #include "nfsd.h"
  49. #include "rpcmisc.h"
  50.  
  51. #define FHTRACE
  52.  
  53. #define hash_psi(psi) (((psi)^((psi)>>8)^((psi)>>16)^((psi)>>24)) & 0xff)
  54.  
  55. static mutex ex_state = inactive;
  56. static mutex io_state = inactive;
  57. #define HASH_TAB_SIZE    256
  58. static fhcache fh_head, fh_tail;
  59. static fhcache *fh_hashed[HASH_TAB_SIZE];
  60. static fhcache *fd_lru_head = NULL,
  61.                *fd_lru_tail = NULL;
  62. static int fh_list_size;
  63. static time_t curtime;
  64.  
  65. #ifndef FOPEN_MAX
  66. #define FOPEN_MAX 256
  67. #endif
  68.  
  69. #ifndef FHTRACE
  70. #undef    D_FHTRACE
  71. #define D_FHTRACE    D_FHCACHE
  72. #endif
  73.  
  74. static fhcache *fd_cache[FOPEN_MAX] = { NULL };
  75. static int fd_cache_size = 0;
  76.  
  77. #ifndef NFSERR_INVAL            /* that Sun forgot */
  78. #define NFSERR_INVAL    22
  79. #endif
  80.  
  81. struct {
  82.     enum nfsstat error;
  83.     int errno;
  84. } nfs_errtbl[]= {
  85.     { NFS_OK,        0        },
  86.     { NFSERR_PERM,        EPERM        },
  87.     { NFSERR_NOENT,        ENOENT        },
  88.     { NFSERR_IO,        EIO        },
  89.     { NFSERR_NXIO,        ENXIO        },
  90.     { NFSERR_ACCES,        EACCES        },
  91.     { NFSERR_EXIST,        EEXIST        },
  92.     { NFSERR_NODEV,        ENODEV        },
  93.     { NFSERR_NOTDIR,    ENOTDIR        },
  94.     { NFSERR_ISDIR,        EISDIR        },
  95.     { NFSERR_INVAL,        EINVAL        },
  96.     { NFSERR_FBIG,        EFBIG        },
  97.     { NFSERR_NOSPC,        ENOSPC        },
  98.     { NFSERR_ROFS,        EROFS        },
  99.     { NFSERR_NAMETOOLONG,    ENAMETOOLONG    },
  100.     { NFSERR_NOTEMPTY,    ENOTEMPTY    },
  101. #ifdef EDQUOT
  102.     { NFSERR_DQUOT,        EDQUOT        },
  103. #endif
  104.     { NFSERR_STALE,        ESTALE        },
  105.     { NFSERR_WFLUSH,    EIO        },
  106.     { -1,            EIO        }
  107. };
  108. int fh_initialized = 0;
  109.  
  110. /* Forward declared local functions */
  111. static _PRO(int path_psi, (char *, nfsstat *, struct stat *, struct stat *));
  112. static _PRO(int fh_flush_fds, (void));
  113. static _PRO(char *fh_dump, (svc_fh *));
  114. static _PRO(void fh_insert_fdcache, (fhcache *fhc));
  115. static _PRO(void fh_unlink_fdcache, (fhcache *fhc));
  116.  
  117. static void fh_move_to_front(fhc)
  118. fhcache *fhc;
  119. {
  120.     /* Remove from current posn */
  121.     fhc->prev->next = fhc->next;
  122.     fhc->next->prev = fhc->prev;
  123.  
  124.     /* Insert at head */
  125.     fhc->prev = &fh_head;
  126.     fhc->next = fh_head.next;
  127.     fhc->prev->next = fhc;
  128.     fhc->next->prev = fhc;
  129. }
  130.  
  131. static void fh_inserthead(fhc)
  132. fhcache *fhc;
  133. {
  134.     register fhcache **hash_slot;
  135.  
  136.     /* Insert at head. */
  137.     fhc->prev = &fh_head;
  138.     fhc->next = fh_head.next;
  139.     fhc->prev->next = fhc;
  140.     fhc->next->prev = fhc;
  141.     fh_list_size++;
  142.  
  143.     /* Insert into hash tab. */
  144.     hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]);
  145.     fhc->hash_next = *hash_slot;
  146.     *hash_slot = fhc;
  147. }
  148.  
  149. static fhcache *fh_lookup(psi)
  150. u_long psi;
  151. {
  152.     register fhcache *fhc;
  153.  
  154.     fhc = fh_hashed[psi % HASH_TAB_SIZE];
  155.     while (fhc != NULL && fhc->h.psi != psi)
  156.         fhc = fhc->hash_next;
  157.     return (fhc);
  158. }
  159.  
  160. static void fh_insert_fdcache(fhc)
  161. fhcache    *fhc;
  162. {
  163.     if (fhc == fd_lru_head)
  164.         return;
  165.     if (fhc->fd_next || fhc->fd_prev)
  166.         fh_unlink_fdcache(fhc);
  167.     if (fd_lru_head)
  168.         fd_lru_head->fd_prev = fhc;
  169.     else
  170.         fd_lru_tail = fhc;
  171.     fhc->fd_next = fd_lru_head;
  172.     fd_lru_head = fhc;
  173.  
  174. #ifdef FHTRACE
  175.     if (fd_cache[fhc->fd] != NULL) {
  176.         dprintf(L_ERROR, "fd cache inconsistency!\n");
  177.         return;
  178.     }
  179. #endif
  180.     fd_cache[fhc->fd] = fhc;
  181.     fd_cache_size++;
  182. }
  183.  
  184. static void fh_unlink_fdcache(fhc)
  185. fhcache    *fhc;
  186. {
  187.     fhcache    *prev = fhc->fd_prev,
  188.         *next = fhc->fd_next;
  189.  
  190.     fhc->fd_next = fhc->fd_prev = NULL;
  191.     if (next) {
  192.         next->fd_prev = prev;
  193.     } else if (fd_lru_tail == fhc) {
  194.         fd_lru_tail = prev;
  195.     } else {
  196.         dprintf(L_ERROR, "fd cache inconsistency\n");
  197.         return;
  198.     }
  199.     if (prev) {
  200.         prev->fd_next = next;
  201.     } else if (fd_lru_head == fhc) {
  202.         fd_lru_head = next;
  203.     } else {
  204.         dprintf(L_ERROR, "fd cache inconsistency\n");
  205.         return;
  206.     }
  207.  
  208. #ifdef FHTRACE
  209.     if (fd_cache[fhc->fd] != fhc) {
  210.         dprintf(L_ERROR, "fd cache inconsistency!\n");
  211.         return;
  212.     }
  213. #endif
  214.     fd_cache[fhc->fd] = NULL;
  215.     fd_cache_size--;
  216. }
  217.  
  218. static void fh_close(fhc)
  219. fhcache *fhc;
  220. {
  221.     if (fhc->fd >= 0) {
  222.         dprintf(D_FHCACHE,
  223.             "fh_close: closing handle %x ('%s', fd=%d)\n",
  224.             fhc, fhc->path ? fhc->path : "<unnamed>", fhc->fd);
  225.         fh_unlink_fdcache(fhc);
  226.         close(fhc->fd);
  227.         fhc->fd = -1;
  228.     }
  229. }
  230.  
  231. static void fh_delete(fhc)
  232. fhcache *fhc;
  233. {
  234.     register fhcache **hash_slot;
  235.  
  236. #ifdef FHTRACE
  237.     if (fhc->h.hash_path[0] == (unsigned char)-1)
  238.         return;
  239. #endif
  240.  
  241.     dprintf(D_FHTRACE|D_FHCACHE,
  242.         "fh_delete: deleting handle %x ('%s', fd=%d)\n",
  243.         fhc, fhc->path ? fhc->path : "<unnamed>", fhc->fd);
  244.  
  245.     /* Remove from current posn */
  246.     fhc->prev->next = fhc->next;
  247.     fhc->next->prev = fhc->prev;
  248.     fh_list_size--;
  249.  
  250.     /* Remove from hash tab */
  251.     hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]);
  252.     while (*hash_slot != NULL && *hash_slot != fhc)
  253.         hash_slot = &((*hash_slot)->hash_next);
  254.     if (*hash_slot == NULL)
  255.         dprintf(L_ERROR,
  256.             "internal inconsistency -- fhc(%x) not in hash table\n",
  257.             fhc);
  258.     else
  259.         *hash_slot = fhc->hash_next;
  260.  
  261.     fh_close(fhc);
  262.  
  263.     /* Free storage. */
  264.     if (fhc->path != NULL)
  265.         free(fhc->path);
  266.  
  267. #ifdef FHTRACE
  268.     /* Safeguard against cache corruption */
  269.     fhc->path = NULL;
  270.     fhc->h.hash_path[0] = -1;
  271. #endif
  272.  
  273.     free(fhc);
  274. }
  275.  
  276. /* Lookup an NFS error code and return UNIX equivalent. */
  277. enum nfsstat nfs_errno()
  278. {
  279.     int i;
  280.  
  281.     for (i = 0; nfs_errtbl[i].error != -1; i++) {
  282.         if (nfs_errtbl[i].errno == errno)
  283.             return (nfs_errtbl[i].error);
  284.     }
  285.     dprintf(L_ERROR, "non-standard errno: %d (%s)\n",
  286.         errno, strerror(errno));
  287.     return (NFSERR_IO);
  288. }
  289.  
  290. /*
  291.  * INODES and DEVICES.  NFS assumes that each file within an NFS mounted
  292.  * file-system has a unique inode number.  Thus to mount an entire file
  293.  * hierarchy, as this server sets out to do, requires mapping from inode/devs
  294.  * to pseudo-inode.  Furthermore mount points must be detected and so that
  295.  *    pseudo-inode("name") == pseudo-inode(direntry("name/../name"))
  296.  * One option is to force the directory entry inode to correspond to the
  297.  * result of the stat call, but this involves stat'ing every directory entry
  298.  * during a readdir.  Instead we force the stat call to corresopnd to the
  299.  * directory entry inode (see inner_getattr).  Of course this technique
  300.  * requires that the parent directory is readable.  If it is not the normal
  301.  * stat call result is used.  There is no chance of conflict because the
  302.  * directory can never be read.
  303.  *
  304.  * In theory unique pseudo-inodes cannot be guaranteed, since inode/dev
  305.  * contains 48 bits of information which must be crammed into an inode
  306.  * number constrained to 32 bits.  Fortunately inodes numbers tend to be
  307.  * small (often < 64k, almost always < 512k)
  308.  */
  309. int pseudo_inode(inode, dev)
  310. u_long inode;
  311. u_short dev;
  312. {
  313.     register dmajor, dminor;
  314.  
  315.     /*
  316.          * Assuming major and minor numbers are small integers,
  317.          * gravitate bits of dmajor & dminor device number to
  318.          * high-order bits of word, to avoid clash with real inode num.
  319.          */
  320.     /* reverse (byte-wise) */
  321.     dmajor = ((dev & 0xf0f) << 4) | ((dev & 0xf0f0) >> 4);
  322.     dmajor = ((dmajor & 0x3333) << 2) | ((dmajor & 0xcccc) >> 2);
  323.     dmajor = ((dmajor & 0x5555) << 1) | ((dmajor & 0xaaaa) >> 1);
  324.  
  325.     /* spread low-16 -> 32 with 0's in even posn */
  326.     dmajor = ((dmajor & 0xff00) << 8) | (dmajor & 0xff);
  327.     dmajor = ((dmajor & 0xf000f0) << 4) | (dmajor & 0xf000f);
  328.     dmajor = ((dmajor & 0xc0c0c0c) << 2) | (dmajor & 0x3030303);
  329.     dmajor = ((dmajor & 0x22222222) << 1) | (dmajor & 0x11111111);
  330.     dminor = (dmajor & 0x5555) << 15;
  331.     dmajor = dmajor & 0x55550000;
  332.  
  333.     /*
  334.     dprintf(D_FHCACHE,
  335.         "pseudo_inode: dev=%d, inode=%d, psi=%d\n", dev, inode,
  336.         (dmajor | dminor) ^ inode);
  337.     */
  338.     return ((dmajor | dminor) ^ inode);
  339. }
  340.  
  341. #if 1
  342. static char *fh_buildpath(h)
  343. svc_fh *h;
  344. {
  345.     int i;
  346.     int psi;
  347.     char *path;
  348.     struct stat sbuf;
  349.     char pathbuf[PATH_MAX + NAME_MAX + 1];
  350.     long cookie_stack[HP_LEN + 1];
  351.     char *slash_stack[HP_LEN];
  352.  
  353.     if (h->hash_path[0] >= HP_LEN) {
  354.         dprintf(L_ERROR, "impossible hash_path[0] value: %s\n", 
  355.                     fh_dump(h));
  356.         return NULL;
  357.     }
  358.  
  359.     if (stat("/", &sbuf) < 0)
  360.         return (NULL);
  361.     psi = pseudo_inode(sbuf.st_ino, sbuf.st_dev);
  362.     if (h->hash_path[0] == 0) {
  363.         if (psi != h->psi)
  364.             return (NULL);
  365.         return xstrdup("/");
  366.     }
  367.     /* else */
  368.     if (hash_psi(psi) != h->hash_path[1])
  369.         return (NULL);
  370.  
  371.     auth_override_uid(ROOT_UID);    /* for x-only dirs */
  372.     strcpy(pathbuf, "/");
  373.     cookie_stack[2] = 0;
  374.     for (i = 2; i <= h->hash_path[0] + 1; i++) {
  375.         DIR *dir;
  376.         struct dirent *dp;
  377.  
  378.     backtrack:
  379.         if (stat(pathbuf, &sbuf) >= 0
  380.             && (dir = opendir(pathbuf)) != NULL) {
  381.             if (cookie_stack[i] != 0)
  382.                 seekdir(dir, cookie_stack[i]);
  383.             while ((dp = readdir(dir))) {
  384.                 if (strcmp(dp->d_name, ".") != 0
  385.                     && strcmp(dp->d_name, "..") != 0) {
  386.                     psi = pseudo_inode(dp->d_ino, sbuf.st_dev);
  387.                     if (i == h->hash_path[0] + 1) {
  388.                         if (psi == h->psi) {
  389.                             /*GOT IT*/
  390.                             strcat(pathbuf, dp->d_name);
  391.                             path = xstrdup(pathbuf);
  392.                             closedir(dir);
  393.                             auth_override_uid(auth_uid);
  394.                             return (path);
  395.                         }
  396.                     } else {
  397.                         if (hash_psi(psi) == h->hash_path[i]) {
  398.                             /*PERHAPS WE'VE GOT IT */
  399.                             cookie_stack[i] = telldir(dir);
  400.                             cookie_stack[i + 1] = 0;
  401.                             slash_stack[i] = pathbuf + strlen(pathbuf);
  402.                             strcpy(slash_stack[i], dp->d_name);
  403.                             strcat(pathbuf, "/");
  404.  
  405.                             closedir(dir);
  406.                             goto deeper;
  407.                         }
  408.                     }
  409.                 }
  410.             }
  411.             /* dp == NULL */
  412.             closedir(dir);
  413.         }
  414.         /* shallower */
  415.         i--;
  416.         if (i < 2) {
  417.             auth_override_uid(auth_uid);
  418.             return (NULL);    /* SEARCH EXHAUSTED */
  419.         }
  420.  
  421.         /* Prune path */
  422.         *(slash_stack[i]) = '\0';
  423.         goto backtrack;
  424.     deeper:
  425.         ;
  426.     }
  427.     auth_override_uid(auth_uid);
  428.     return (NULL);        /* actually not reached */
  429. }
  430.  
  431. #else
  432. /* This code is somewhat more readable (and safer) but doesn't work yet */
  433. static int fh_buildcomp(h, dev, dir, i, path)
  434. svc_fh *h;
  435. dev_t dev;
  436. DIR *dir;
  437. int i;
  438. char *path;
  439. {
  440.     struct dirent *dp;
  441.     int psi, len;
  442.  
  443.     while ((dp = readdir(dir))) {
  444.         if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
  445.             continue;
  446.         psi = pseudo_inode(dp->d_ino, dev);
  447.         if (i == h->hash_path[0] + 1) {
  448.             if (psi != h->psi)
  449.                 continue;
  450.  
  451.             /*GOT IT*/
  452.             len = strlen(path);
  453.             if (len + strlen(dp->d_name) >= PATH_MAX + NAME_MAX)
  454.                 continue; /* shucks */
  455.  
  456.             strcat(path, dp->d_name);
  457.             return 1;
  458.         } else if (hash_psi(psi) == h->hash_path[i]) {
  459.             /* PERHAPS WE'VE GOT IT */
  460.  
  461.             len = strlen(path);
  462.             if (len + strlen(dp->d_name) + 1 >= PATH_MAX + NAME_MAX)
  463.                 continue;
  464.  
  465.             strcpy(path + len, dp->d_name);
  466.             strcpy(path + len, "/");
  467.             return 1;
  468.         }
  469.     }
  470.     return 0;
  471. }
  472.  
  473. static char *fh_buildpath(h)
  474. svc_fh *h;
  475. {
  476.     int i;
  477.     int psi;
  478.     char *path;
  479.     struct stat sbuf;
  480.     char pathbuf[PATH_MAX + NAME_MAX + 1];
  481.     long cookie_stack[HP_LEN + 1];
  482.     char *slash_stack[HP_LEN];
  483.  
  484.     if (h->hash_path[0] >= HP_LEN) {
  485.         dprintf(L_ERROR, "impossible hash_path[0] value: %s\n", 
  486.                     fh_dump(h));
  487.         return NULL;
  488.     }
  489.  
  490.     if (stat("/", &sbuf) < 0)
  491.         return (NULL);
  492.     psi = pseudo_inode(sbuf.st_ino, sbuf.st_dev);
  493.  
  494.     if (h->hash_path[0] == 0) {
  495.         if (psi != h->psi)
  496.             return (NULL);
  497.         return xstrdup("/");
  498.     }
  499.     if (hash_psi(psi) != h->hash_path[1])
  500.         return (NULL);
  501.  
  502.     auth_override_uid(ROOT_UID);
  503.     strcpy(pathbuf, "/");
  504.     i = 2;
  505.     cookie_stack[i] = 0;
  506.     while (i <= h->hash_path[0] + 1) {
  507.         DIR *dir;
  508.  
  509.         if (stat(pathbuf, &sbuf) >= 0
  510.             && (dir = opendir(pathbuf)) != NULL) {
  511.             if (cookie_stack[i] != 0)
  512.                 seekdir(dir, cookie_stack[i]);
  513.             if (!fh_buildcomp(h, sbuf.st_dev, dir, i, pathbuf)) {
  514.                 closedir(dir);
  515.                 goto shallower;
  516.             }
  517.             if (i != h->hash_path[0] + 1) {
  518.                 /* more components to go */
  519.                 slash_stack[i] = pathbuf + strlen(pathbuf);
  520.                 cookie_stack[i] = telldir(dir);
  521.                 cookie_stack[i + 1] = 0;
  522.                 closedir(dir);
  523.                 i++;
  524.                 continue;
  525.             }
  526.             path = xstrdup(pathbuf);
  527.             closedir(dir);
  528.             auth_override_uid(auth_uid);
  529.             return (path);
  530.         }
  531.     shallower:
  532.         if (--i < 2)
  533.             break;
  534.         /* Prune path */
  535.         *(slash_stack[i]) = '\0';
  536.     }
  537.     auth_override_uid(auth_uid);
  538.     return (NULL);
  539. }
  540. #endif
  541.  
  542. static int path_psi(path, status, sbp, tsbp)
  543. char *path;
  544. nfsstat *status;
  545. struct stat *sbp;
  546. struct stat *tsbp;
  547. {
  548.     struct stat sbuf;
  549.  
  550.     if (sbp == NULL)
  551.         sbp = &sbuf;
  552.     if (lstat(path, sbp) < 0) {
  553.         *status = nfs_errno();
  554.         return (0);
  555.     }
  556.     if (tsbp)
  557.         *tsbp = *sbp;
  558.     if (S_ISDIR(sbp->st_mode) && strcmp(path, "/") != 0) {
  559.         /* Special case for directories--test for mount point. */
  560.         struct stat ddbuf;
  561.         char *sindx;
  562.         char *fname;
  563.         char squirrel;
  564.  
  565.         /* Find start of last component of path. */
  566.         if ((sindx = strrchr(path, '/')) == path) {
  567.             sindx++;
  568.             fname = sindx;
  569.         } else
  570.             fname = sindx + 1;
  571.  
  572.         /* Remove last element of path. */
  573.         squirrel = *sindx;
  574.         *sindx = '\0';
  575.         if (lstat(path, &ddbuf) < 0) {
  576.             *sindx = squirrel;
  577.             *status = nfs_errno();
  578.             return (0);
  579.         }
  580.         /* Sindx now points to directory entry name. */
  581.         if (ddbuf.st_dev != sbp->st_dev) {
  582.             /* Directory is a mount point. */
  583.             DIR *dirp;
  584.             struct dirent *dp;
  585.  
  586.             errno = 0;
  587.             if ((dirp = opendir(path)) == NULL) {
  588.                 *sindx = squirrel;    /* restore path */
  589.                 if (errno == EACCES)
  590.                     goto unreadable;
  591.                 if (errno != 0)
  592.                     *status = nfs_errno();
  593.                 else
  594.                     *status = NFSERR_NOENT;
  595.             } else {
  596.                 *sindx = squirrel;    /* restore path */
  597.                 *status = NFS_OK;
  598.                 do {
  599.                     if ((dp = readdir(dirp)) == NULL) {
  600.                         *status = NFSERR_NOENT;
  601.                         closedir(dirp);
  602.                         return (0);
  603.                     }
  604.                 } while (strcmp(fname, dp->d_name) != 0);
  605.                 sbp->st_dev = ddbuf.st_dev;
  606.                 sbp->st_ino = dp->d_ino;
  607.                 closedir(dirp);
  608.             }
  609.         } else
  610.             *sindx = squirrel;    /* restore path */
  611.     unreadable:
  612.         ;
  613.     }
  614.     return (pseudo_inode(sbp->st_ino, sbp->st_dev));
  615. }
  616.  
  617. fhcache *fh_find(h, mode)
  618. svc_fh *h;
  619. int mode;
  620. {
  621.     char buff[1024], *sp;
  622.     register fhcache *fhc, *flush;
  623.  
  624. #ifdef FHTRACE
  625.     if (h->hash_path[0] >= HP_LEN) {
  626.         dprintf(L_ERROR, "stale fh detected: %s\n", fh_dump(h));
  627.         return NULL;
  628.     }
  629. #endif
  630.  
  631.     sprintf(buff, "fh_find: psi=%lu... ", (unsigned long) h->psi);
  632.     sp = buff + strlen(buff);
  633.     ex_state = active;
  634.     time(&curtime);
  635.     while ((fhc = fh_lookup(h->psi)) != NULL) {
  636.         dprintf(D_FHCACHE, "%s found '%s', fd=%d\n", buff,
  637.             fhc->path ? fhc->path : "<unnamed>",
  638.             fhc->fd);
  639.  
  640.         /* But what if hash_paths are not the same? Something is stale. */
  641.         if (memcmp(h->hash_path, fhc->h.hash_path, HP_LEN) != 0) {
  642.             dprintf(D_FHTRACE, "fh_find: stale fh (path mismatch)\n");
  643. #ifdef FHTRACE
  644.             dprintf(D_FHTRACE, "\tdata: %s\n", fh_dump(h));
  645. #endif
  646.             if (mode != FHFIND_FCREATE) {
  647.                 ex_state = inactive;
  648.                 return (NULL);
  649.             }
  650.             fh_delete(fhc);
  651.             dprintf(D_FHCACHE,
  652.                 "fh_find: deleted old handle... sortof\n");
  653.             break;
  654.         }
  655.         if (fhc != fh_head.next)
  656.             fh_move_to_front(fhc);
  657.         fhc->last_used = curtime;
  658.         ex_state = inactive;
  659.         return (fhc);
  660.     }
  661.  
  662.     dprintf(D_FHCACHE, "%s not found.\n", buff);
  663.     if (mode == FHFIND_FCACHED) {
  664.         ex_state = inactive;
  665.         return NULL;
  666.     }
  667.  
  668.     for (flush = fh_tail.prev; fh_list_size > FH_CACHE_LIMIT; flush = fhc) {
  669.         /* Don't flush current head. */
  670.         if (flush == &fh_head)
  671.             break;
  672.         fhc = flush->prev;
  673.         fh_delete(flush);
  674.     }
  675.     fhc = (fhcache *) xmalloc(sizeof *fhc);
  676.     if (mode == FHFIND_FCREATE) {
  677.         /* File will be created */
  678.         fhc->path = NULL;
  679.     } else {
  680.         /* File must exist. Attempt to construct from hash_path */
  681.         char *path;
  682.  
  683.         if ((path = fh_buildpath(h)) == NULL) {
  684. #ifdef FHTRACE
  685.             dprintf(D_FHTRACE, "fh_find: stale fh (hash path)\n");
  686.             dprintf(D_FHTRACE, "\tdata: %s\n", fh_dump(h));
  687. #endif
  688.             free(fhc);
  689.             ex_state = inactive;
  690.             return NULL;
  691.         }
  692.         fhc->path = path;
  693.     }
  694.     fhc->flags = 0;
  695.     if (fhc->path) {
  696.         struct stat    stb;
  697.  
  698.         if (stat(fhc->path, &stb) >= 0) {
  699.             if (re_export && nfsmounted(fhc->path, &stb))
  700.                 fhc->flags |= FHC_NFSMOUNTED;
  701.         }
  702.     }
  703.     fhc->fd = -1;
  704.     fhc->last_used = curtime;
  705.     fhc->h = *h;
  706.     fhc->last_clnt = NULL;
  707.     fhc->last_mount = NULL;
  708.     fhc->last_uid = (uid_t)-1;
  709.     fhc->fd_next = fhc->fd_prev = NULL;
  710.     fh_inserthead(fhc);
  711.     dprintf(D_FHCACHE,
  712.         "fh_find: created new handle %x ('%s')\n",
  713.         fhc, fhc->path ? fhc->path : "<unnamed>");
  714.     ex_state = inactive;
  715.     if (fh_list_size > FH_CACHE_LIMIT)
  716.         flush_cache(0);
  717. #ifdef FHTRACE
  718.     if (fhc->h.hash_path[0] == 0xFF) {
  719.         dprintf(L_ERROR, "newly created fh instantly flushed?!");
  720.         return NULL;
  721.     }
  722. #endif
  723.     return (fhc);
  724. }
  725.  
  726. /*
  727.  * This function is usually called from the debugging code, where
  728.  * the user has not been authenticated yet. Hence, no path lookups.
  729.  */
  730. char *fh_pr(fh)
  731. nfs_fh *fh;
  732. {
  733.     fhcache *h;
  734.  
  735.     if ((h = fh_find((svc_fh *) fh, FHFIND_FCACHED)) == NULL)
  736.         return fh_dump((svc_fh *) fh);
  737.     return (h->path);
  738. }
  739.  
  740. static char *fh_dump(fh)
  741. svc_fh *fh;
  742. {
  743.     static char    buf[65];
  744.     char        *sp;
  745.     int        i, n = fh->hash_path[0];
  746.  
  747.     sprintf(buf, "%08lx %02x ", fh->psi, fh->hash_path[0]);
  748.     for (i = 1, sp = buf + 12; i <= n && i < HP_LEN; i++, sp += 2)
  749.         sprintf(sp, "%02x", fh->hash_path[i]);
  750.     return buf;
  751. }
  752.  
  753. /*
  754.  * This routine is only used by the mount daemon.
  755.  * It creates the initial file handle.
  756.  */
  757. int fh_create(fh, path)
  758. nfs_fh *fh;
  759. char *path;
  760. {
  761.     svc_fh *key = (svc_fh *) fh;
  762.     fhcache *h;
  763.     int psi;
  764.     nfsstat status;
  765.     char *s;
  766.  
  767.     memset((char *) fh, 0, sizeof (nfs_fh));
  768.     key->hash_path[0] = 0;
  769.     status = NFS_OK;
  770.     if ((psi = path_psi("/", &status, NULL, NULL)) == 0)
  771.         return ((int) status);
  772.     s = path;
  773.     while ((s = strchr(s + 1, '/')) != NULL) {
  774.         if (++(key->hash_path[0]) >= HP_LEN)
  775.             return ((int) NFSERR_NAMETOOLONG);
  776.         key->hash_path[key->hash_path[0]] = hash_psi(psi);
  777.         *s = '\0';
  778.         if ((psi = path_psi(path, &status, NULL, NULL)) == 0)
  779.             return ((int) status);
  780.         *s = '/';
  781.     }
  782.     if (*(strrchr(path, '/') + 1) != '\0') {
  783.         if (++(key->hash_path[0]) >= HP_LEN)
  784.             return ((int) NFSERR_NAMETOOLONG);
  785.         key->hash_path[key->hash_path[0]] = hash_psi(psi);
  786.         if ((psi = path_psi(path, &status, NULL, NULL)) == 0)
  787.             return ((int) status);
  788.     }
  789.     key->psi = psi;
  790.     h = fh_find(key, FHFIND_FCREATE);
  791.  
  792. #ifdef FHTRACE
  793.     if (!h)
  794.         return NFSERR_STALE;
  795. #endif
  796.  
  797.     /* assert(h != NULL); */
  798.     if (h->path == NULL) {
  799.         h->fd = -1;
  800.         h->path = xstrdup(path);
  801.         h->flags = 0;
  802.     }
  803.     return ((int) status);
  804. }
  805.  
  806. char *fh_path(fh, status)
  807. nfs_fh *fh;
  808. nfsstat *status;
  809. {
  810.     fhcache *h;
  811.  
  812.     if ((h = fh_find((svc_fh *) fh, FHFIND_FEXISTS)) == NULL) {
  813.         *status = NFSERR_STALE;
  814.         return (NULL);
  815.     }
  816.     *status = NFS_OK;
  817.     return (h->path);
  818. }
  819.  
  820. nfs_fh *fh_handle(h)
  821. fhcache *h;
  822. {
  823.     return ((nfs_fh*)&(h->h));
  824. }
  825.  
  826. int path_open(path, omode, perm)
  827. char *path;
  828. int omode;
  829. int perm;
  830. {
  831.     int fd;
  832.     int oerrno, ok;
  833.     struct stat buf;
  834.  
  835.     fh_flush_fds();
  836.  
  837.     /* If the file exists, make sure it is a regular file. Opening
  838.      * device files might hang the server. There's still a tiny window
  839.      * here, but it's not very likely someone's able to exploit
  840.      * this.
  841.      */
  842.     if ((ok = (lstat(path, &buf) >= 0)) && !S_ISREG(buf.st_mode)) {
  843.         errno = EISDIR;    /* emulate SunOS server */
  844.         return -1;
  845.     }
  846.  
  847. #if 1
  848.     fd = open(path, omode, perm);
  849. #else
  850.     /* First, try to open the file read/write. The O_*ONLY flags ored
  851.      * together do not yield O_RDWR, unfortunately. 
  852.      * Backed out for now; we have to record the new omode in
  853.      * h->omode to be effective, anyway.
  854.      */
  855.     fd = open(path, (omode & ~O_ACCMODE)|O_RDWR, perm);
  856.     if (fd < 0)
  857.         fd = open(path, omode, perm);
  858. #endif
  859.  
  860.     oerrno = errno;
  861.  
  862.     /* The file must exist at this point. */
  863.     if (!ok && lstat(path, &buf) < 0) {
  864.         /*
  865.         dprintf(L_ERROR,
  866.             "path_open(%s, %o, %o): failure mode 1, err=%d\n",
  867.             path, omode, perm, errno);
  868.          */
  869.         errno = oerrno;
  870.         return -1;
  871.     }
  872.  
  873.     /* Do some serious cheating for statelessness. The following accomp-
  874.      * lishes two things: first, it gives the file owner r/w access to
  875.      * the file whatever the permissions are, so that files are still
  876.      * accessible after an fchown(fd, 0). The second part of the
  877.      * condition allows read access to mode 0111 executables.
  878.      *
  879.      * The old conditon read like this:
  880.      * if (fd < 0 && oerrno == EACCES) {
  881.      *    if (oerrno == EACCES && (buf.st_uid == auth_uid
  882.      *        || (omode == O_RDONLY && (buf.st_mode & S_IXOTH)))) {
  883.      *        override uid; etc...
  884.      *    }
  885.      * }
  886.      * This would truncate read-only files on creat() calls. Now
  887.      * ftruncate(fd, 0) should still be legal for the user when the
  888.      * file was chmoded *after* opening it, but we have no way to tell,
  889.      * and a semi-succeding `cp foo readonly-file' is much more
  890.      * unintuitive and destructive than a failing ftruncate().
  891.      */
  892.     if (fd < 0 && oerrno == EACCES && !(omode & (O_CREAT|O_TRUNC))) {
  893.         if ((buf.st_uid == auth_uid && (omode & O_ACCMODE) == omode)
  894.          || ((buf.st_mode & S_IXOTH) && omode == O_RDONLY)) {
  895.             auth_override_uid(ROOT_UID);
  896.             fd = open(path, omode, perm);
  897.             oerrno = errno;
  898.             auth_override_uid(auth_uid);
  899.         }
  900.     }
  901.  
  902.     if (fd < 0) {
  903.         dprintf(D_FHCACHE,
  904.             "path_open(%s, %o, %o): failure mode 2, err=%d, oerr=%d\n",
  905.             path, omode, perm, errno, oerrno);
  906.         errno = oerrno;
  907.         return -1;
  908.     }
  909.  
  910.     errno = oerrno;
  911.     return (fd);
  912. }
  913.  
  914. int fh_fd(h, status, omode)
  915. fhcache *h;
  916. nfsstat *status;
  917. int omode;
  918. {
  919.     int    retry;
  920.  
  921.     if (h->fd >= 0) {
  922.         /* If the requester's uid doesn't match that of the user who
  923.          * opened the file, we close the file. I guess we could work
  924.          * some magic with the eaccess stuff, but I don't know if
  925.          * this would be any faster than simply re-doing the open.
  926.          */
  927.         if (h->last_uid == auth_uid && (h->omode == omode ||
  928.             ((omode == O_RDONLY || omode == O_WRONLY) && h->omode == O_RDWR))) {
  929.             dprintf(D_FHCACHE, "fh_fd: reusing fd=%d\n", h->fd);
  930.             fh_insert_fdcache(h);    /* move to front of fd LRU */
  931.             return (h->fd);
  932.         }
  933.         dprintf(D_FHCACHE,
  934.             "fh_fd: uid/omode mismatch (%d/%d wanted, %d/%d cached)\n",
  935.              auth_uid, omode, h->last_uid, h->omode);
  936.         fh_close(h);
  937.     }
  938.     errno = 0;
  939.     if (!h->path) {
  940.         *status = NFSERR_STALE;
  941.         return (-1);    /* something is really hosed */
  942.     }
  943.     for (retry = 0; retry < 2; retry++) {
  944.         if ((h->fd = path_open(h->path, omode, 0)) >= 0) {
  945.             io_state = active;
  946.             h->omode = omode & O_ACCMODE;
  947.             fh_insert_fdcache(h);
  948.             dprintf(D_FHCACHE, "fh_fd: new open as fd=%d\n", h->fd);
  949.             h->last_uid = auth_uid;
  950.             return (h->fd);
  951.         } 
  952.         dprintf(D_FHCACHE, "fh_fd: open failed.\n");
  953.         *status = nfs_errno();
  954.         if (errno != ENOENT)
  955.             return -1;
  956.  
  957.         /* maybe the cached path is stale */
  958.         free(h->path);
  959.         h->path = NULL;
  960.         if (!retry && !fh_buildpath(h))
  961.             break;
  962.     }
  963.     return -1;
  964. }
  965.  
  966. void fd_inactive(fd)
  967. int fd;
  968. {
  969.     io_state = inactive;
  970. }
  971.  
  972. nfsstat fh_compose(dopa, new_fh, sbpp, fd, omode)
  973. diropargs *dopa;
  974. nfs_fh *new_fh;
  975. struct stat **sbpp;
  976. int fd;
  977. int omode;
  978. {
  979.     svc_fh *key;
  980.     fhcache *dirh, *h;
  981.     char *sindx;
  982.     int is_dd;
  983.     nfsstat ret;
  984.     struct stat tsbuf;
  985.     char pathbuf[PATH_MAX + NAME_MAX + 1];
  986.  
  987.     if ((dirh = fh_find((svc_fh *) & (dopa->dir), FHFIND_FEXISTS)) == NULL)
  988.         return (NFSERR_STALE);
  989.  
  990.     /* This allows only single directories to be looked up, could be
  991.        a bit more sophisticated, but i don't know if that is neccesary */
  992.     if (strchr(dopa->name, '/') != NULL)
  993.         return(NFSERR_ACCES);
  994.  
  995.     /* Construct path.
  996.      * Lookups of "" generated by broken OS/2 clients
  997.      */
  998.     if (strcmp(dopa->name, ".") == 0 || dopa->name[0] == '\0') {
  999.         *new_fh = dopa->dir;
  1000.         *sbpp = NULL;
  1001.         return (NFS_OK);
  1002.     }
  1003.     if (strcmp(dopa->name, "..") == 0) {
  1004.         is_dd = 1;
  1005.         sindx = strrchr(dirh->path, '/');
  1006.         if (sindx == dirh->path)
  1007.             strcpy(pathbuf, "/");
  1008.         else {
  1009.             int len = sindx - dirh->path;
  1010.             strncpy(pathbuf, dirh->path, len);
  1011.             pathbuf[len] = '\0';
  1012.         }
  1013.     }
  1014.     else if (!re_export && (dirh->flags & FHC_NFSMOUNTED))
  1015.         return (NFSERR_NOENT);
  1016.     else {
  1017.         int len = strlen(dirh->path);
  1018.  
  1019.         is_dd = 0;
  1020.         if (dirh->path[len - 1] == '/')
  1021.             len--;
  1022.         strncpy(pathbuf, dirh->path, len);
  1023.         pathbuf[len] = '/';
  1024.         strcpy(pathbuf + (len + 1), dopa->name);
  1025.     }
  1026.  
  1027.     *new_fh = dopa->dir;
  1028.     key = (svc_fh *) new_fh;
  1029.     if ((key->psi = path_psi(pathbuf, &ret, *sbpp, &tsbuf)) == 0)
  1030.         return (ret);
  1031.  
  1032.     if (is_dd) {
  1033.         /* Don't cd .. from root, or mysterious ailments will
  1034.          * befall your fh cache... Fixed. */
  1035.         if (key->hash_path[0] > 0)
  1036.             key->hash_path[key->hash_path[0]--] = 0;
  1037.     } else {
  1038.         if (++(key->hash_path[0]) >= HP_LEN)
  1039.             return (NFSERR_NAMETOOLONG);
  1040.         key->hash_path[key->hash_path[0]] = hash_psi(dirh->h.psi);
  1041.     }
  1042.     h = fh_find(key, FHFIND_FCREATE);
  1043.  
  1044. #ifdef FHTRACE
  1045.     if (h == NULL)
  1046.         return NFSERR_STALE;
  1047.     if (h->h.hash_path[0] >= HP_LEN) {
  1048.         dprintf(L_ERROR, "fh cache corrupted! file %s hplen %02x",
  1049.                     h->path? h->path : "<unnamed>",
  1050.                     h->h.hash_path[0]);
  1051.         return NFSERR_STALE;
  1052.     }
  1053. #endif
  1054.  
  1055.     /* New code added by Don Becker */
  1056.     if ((h->path != NULL) && (strcmp(h->path, pathbuf) != 0)) {
  1057.         /* We must have cached an old file under the same inode # */
  1058.         dprintf(D_FHTRACE, "Disposing of fh with bad path.\n");
  1059.         fh_delete(h);
  1060.         h = fh_find(key, FHFIND_FCREATE);
  1061. #ifdef FHTRACE
  1062.         if (!h) return NFSERR_STALE;
  1063. #endif
  1064.         if (h->path)
  1065.             dprintf(L_ERROR, "Internal inconsistency: double entry (path '%s', now '%s').\n",
  1066.                 h->path, pathbuf);
  1067.     }
  1068.     dprintf(D_FHCACHE, "fh_compose: using  handle %x ('%s', fd=%d)\n",
  1069.         h, h->path ? h->path : "<unnamed>", h->fd);
  1070.     /* End of new code */
  1071.  
  1072.     /* assert(h != NULL); */
  1073.     if (h->path == 0) {
  1074.         h->path = xstrdup(pathbuf);
  1075.         h->flags = 0;
  1076.         if (!re_export && nfsmounted(pathbuf, &tsbuf))
  1077.             h->flags |= FHC_NFSMOUNTED;
  1078. #ifdef FHTRACE
  1079.         dprintf(D_FHTRACE, "fh_compose: created handle %s\n", h->path);
  1080.         dprintf(D_FHTRACE, "\tdata: %s\n", fh_dump(&h->h));
  1081. #else
  1082.         dprintf(D_FHCACHE,
  1083.             "fh_compose: +using  handle %x ('%s', fd=%d)\n",
  1084.             h, h->path, h->fd);
  1085. #endif
  1086.     }
  1087.  
  1088.     if (fd >= 0) {
  1089.         dprintf(D_FHCACHE,
  1090.             "fh_compose: handle %x using passed fd %d\n", h, fd);
  1091.         if (h->fd >= 0)
  1092.             fh_close(h);
  1093.         h->fd = fd;
  1094.         fh_insert_fdcache(h);
  1095.         dprintf(D_FHCACHE,
  1096.             "fh_compose: +using  handle %x ('%s', fd=%d)\n",
  1097.             h, h->path ? h->path : "<unnamed>", h->fd);
  1098.     }
  1099.     if (omode >= 0)
  1100.         h->omode = omode & O_ACCMODE;
  1101.     return (NFS_OK);
  1102. }
  1103.  
  1104. int fh_psi(fh)
  1105. nfs_fh *fh;
  1106. {
  1107.     svc_fh *h = (svc_fh *) fh;
  1108.     return (h->psi);
  1109. }
  1110.  
  1111. void fh_remove(path)
  1112. char *path;
  1113. {
  1114.     int psi;
  1115.     nfsstat status;
  1116.     fhcache *fhc;
  1117.  
  1118.     psi = path_psi(path, &status, NULL, NULL);
  1119.     if (psi == 0)
  1120.         return;
  1121.     ex_state = active;
  1122.     fhc = fh_lookup(psi);
  1123.     if (fhc != NULL)
  1124.         fh_delete(fhc);
  1125.  
  1126.     ex_state = inactive;
  1127.     return;
  1128. }
  1129.  
  1130. /*
  1131.  * Close a file to make an fd available for a new file.
  1132.  */
  1133. static int fh_flush_fds()
  1134. {
  1135.     if (io_state == active) {
  1136.         dprintf(D_FHCACHE, "fh_flush_fds: not flushing... io active\n");
  1137.         return (-1);
  1138.     }
  1139.     while (fd_cache_size >= FD_CACHE_LIMIT)
  1140.         fh_close(fd_lru_tail);
  1141.     return (0);
  1142. }
  1143.  
  1144. /*
  1145.  * fh_flush() is invoked periodically from SIGALRM, and on
  1146.  * demand from fh_find.  A simple form of mutual exclusion
  1147.  * protects this routine from multiple concurrent executions.
  1148.  * Since the preemption that occurs when a signal is received
  1149.  * is one-sided, we do need an atomic test and set.  If the
  1150.  * signal arrives between the test and the set, the first
  1151.  * invocation safely stalls until the signal-caused invocation
  1152.  * completes.
  1153.  *
  1154.  * NOTE: fh_flush is now always called from the top RPC dispatch
  1155.  * routine, and the ex_state stuff is likely to go when this proves
  1156.  * to work.
  1157.  */
  1158. void fh_flush(force)
  1159. int force;
  1160. {
  1161.     register fhcache *h;
  1162.  
  1163. #ifdef DEBUG
  1164.     time_t now;
  1165.     time(&now);
  1166.     dprintf(D_FHTRACE, "flushing cache at %s: state = %s\n",
  1167.         ctime(&now), (ex_state == inactive) ? "inactive" : "active");
  1168. #endif
  1169.  
  1170.     if (ex_state == inactive) {
  1171.         int cache_size = 0;
  1172.  
  1173.         ex_state = active;
  1174.         time(&curtime);
  1175.         /* Single execution thread */
  1176.  
  1177.         /* works in empty case because: fh_tail.next = &fh_tail */
  1178.         h = fh_head.next;
  1179.         while (h != &fh_tail) {
  1180.             if (cache_size > FH_CACHE_LIMIT
  1181.                 || curtime > h->last_used + DISCARD_INTERVAL
  1182.                 || force) {
  1183.                 h = h->next;
  1184.                 fh_delete(h->prev);
  1185.             } else {
  1186.                 if (h->fd >= 0 &&
  1187.                     curtime > h->last_used + CLOSE_INTERVAL)
  1188.                     fh_close(h);
  1189.                 cache_size++;
  1190.                 h = h->next;
  1191.             }
  1192.         }
  1193.         if (fh_list_size != cache_size)
  1194.             dprintf(L_ERROR,
  1195.                 "internal inconsistency (fh_list_size=%d) != (cache_size=%d)\n",
  1196.                 fh_list_size, cache_size);
  1197.         fh_list_size = cache_size;
  1198.         ex_state = inactive;
  1199.     }
  1200. }
  1201.  
  1202. RETSIGTYPE flush_cache(sig)
  1203. int sig;
  1204. {
  1205.     static volatile int    inprogress = 0;
  1206.  
  1207.     signal (SIGALRM, flush_cache);
  1208.     if (_rpcsvcdirty) {
  1209.         alarm(BUSY_RETRY_INTERVAL);
  1210.         need_flush = 1;
  1211.         return;
  1212.     }
  1213.     if (inprogress++)
  1214.         return;
  1215.     fh_flush(0);
  1216.     inprogress = 0;
  1217.     need_flush = 0;
  1218.     alarm(FLUSH_INTERVAL);
  1219. }
  1220.  
  1221. void fh_init()
  1222. {
  1223.     if (fh_initialized)
  1224.         return;
  1225.     fh_initialized = 1;
  1226.  
  1227.     fh_head.next = fh_tail.next = &fh_tail;
  1228.     fh_head.prev = fh_tail.prev = &fh_head;
  1229.     /* last_flushable = &fh_tail; */
  1230.  
  1231.     signal(SIGALRM, flush_cache);
  1232.     alarm(FLUSH_INTERVAL);
  1233.  
  1234.     umask(0);
  1235. }
  1236.  
  1237.